#include <sys/epoll.h>

#include <errno.h>
#include <iostream>
#include <unistd.h>

#include "SSCF.h"
#include "MutexLocker.h"

#define LOGGER_NAME "shadow.sscf.ssocket"

using std::cout;
using std::endl;

SSCF::SSCF() {
	_socket_idel_timeout = -1;
	_read_evnet_handler = NULL;
	_read_evnet_handler_arg = NULL;
	_logger = log4cxx::Logger::getLogger(LOGGER_NAME);
	pthread_mutex_init(&_mutex, NULL);
}

SSCF::~SSCF() { }


int SSCF::init(int listen_port, int max_thread_num, int socket_idel_timeout, SSCF_READ_EVENT_HANDLER read_event_handler, \
				void * read_evnet_handler_arg) {
	_listen_port = listen_port;
	_max_thread_num = max_thread_num;
	_socket_idel_timeout = socket_idel_timeout;
	_read_evnet_handler = read_event_handler;
	_read_evnet_handler_arg = read_evnet_handler_arg;
	return 1;
}

int SSCF::start_svc() {
	int res = start_listen_thread();
	if (res < 0) {
		LOG4CXX_ERROR(_logger, "start listen thread error, res:" << res);
		return -1;
	}
	res = start_check_socket_timeout_thread();
	if (res < 0) {
		LOG4CXX_ERROR(_logger, "start check socket timeout thread error, res:" << res);
		return -2;
	}
	res = start_main_svc_thread();
	if (res < 0) {
		LOG4CXX_ERROR(_logger, "start socket main svc thread error, res:" << res);
		return -3;
	}
	pthread_join(_listen_thread, NULL);
	pthread_join(_check_socket_timeout_thread, NULL);
	for (list<pthread_t>::const_iterator iter = _main_svc_threads.begin(); _main_svc_threads.end() != iter; iter++) {
		pthread_join(*iter, NULL);
	}
	LOG4CXX_WARN(_logger, "shadow socket communication frame work's all thread exit");
	return 1;
}	

int SSCF::start_listen_thread() {
	int res = _listen_socket.listen(_listen_port);
	if (res < 0) return -1;
	res = pthread_create(&_listen_thread, NULL, listen_thread_routine, this);
	if (res < 0) {
		LOG4CXX_ERROR(_logger, "start listen thread error, errno:" << errno);
		return -2;
	}
	return 1;
}

int SSCF::start_check_socket_timeout_thread() {
	int res = pthread_create(&_check_socket_timeout_thread, NULL, check_socket_timeout_thread_routine, this);
	if (res < 0) {
		LOG4CXX_ERROR(_logger, "start check socket timeout thread error, errno:" << errno);
		return -1;
	}
	return 1;

}

int SSCF::start_main_svc_thread() {
	for (int i = 0; i < _max_thread_num; i++) {
		pthread_t t;
		int res = pthread_create(&t, NULL, main_svc_thread_routine, this);
		if (res < 0) {
			LOG4CXX_ERROR(_logger, "start main svc thread error, errno:" << errno);
			return -1;
		}
		_main_svc_threads.push_back(t);
	}
	return 1;
}

int SSCF::add_socket_to_svc_sockets(SSocket * ps) {
	if (NULL == ps) {
		LOG4CXX_ERROR(_logger, "can not add null socket object to svc sockets");
		return -1;
	}
	if (!ps->is_connected()) {
		LOG4CXX_ERROR(_logger, "can not add a not connected socket to svc sockets");
		return -2;
	}
	MutexLocker locker(&_mutex);
	locker.lock();
	_sockets.insert(map<int, SSocket *>::value_type(ps->get_fd(), ps));
	return 1;
}

void * SSCF::listen_thread_routine(void * arg) {
	SSCF * psscf = (SSCF *)arg;
	LOG4CXX_INFO(psscf->_logger, "listen thread started");
	while (true) {
		SSocket * ps = new SSocket;
		int res = psscf->_listen_socket.accept(*ps);
		if (res > 0) {
			psscf->add_socket_to_svc_sockets(ps);
		}
	}
	LOG4CXX_WARN(psscf->_logger, "listen thread exited");
	return NULL;
}


void * SSCF::check_socket_timeout_thread_routine(void * arg) {
	SSCF * psscf = (SSCF *)arg;
	LOG4CXX_INFO(psscf->_logger, "check socket timeout thread started");
	while (true) {
		map<int, SSocket *> need_to_remove_sockets;
		time_t now = time(NULL);
		MutexLocker locker(&psscf->_mutex);
		locker.lock();
		for (map<int, SSocket *>::const_iterator iter = psscf->_sockets.begin(); \
				psscf->_sockets.end() != iter; iter++) {
			if (psscf->_socket_idel_timeout > 0 && \
					((now - iter->second->get_last_data_input_time()) > psscf->_socket_idel_timeout) && \
					((now - iter->second->get_last_data_output_time()) > psscf->_socket_idel_timeout) \
					&& ((now - iter->second->get_connected_time()) > psscf->_socket_idel_timeout)) {
				need_to_remove_sockets.insert(*iter);
			}
		}
		for(map<int, SSocket *>::const_iterator iter = need_to_remove_sockets.begin(); \
				need_to_remove_sockets.end() != iter; iter++) {
			psscf->_sockets.erase(iter->first);
			iter->second->close();
			delete iter->second;
		}
		locker.unlock();
		sleep(DEFAULT_CHECK_SOCKET_TIMEOUT_INTERVAL);
	}
	LOG4CXX_WARN(psscf->_logger, "check socket timeout thread exited");
	return NULL;
}

void * SSCF::main_svc_thread_routine(void *arg) {
	SSCF * psscf = (SSCF *)arg;
	LOG4CXX_INFO(psscf->_logger, "main svc thread started");
	while(true) {
		int epfd = epoll_create(EPOLL_DEFAULT_SIZE);
		if (-1 == epfd) {
			LOG4CXX_ERROR(psscf->_logger, "epoll create error, errno:" << errno);
			LOG4CXX_WARN(psscf->_logger, "main svc thread exited");
			return NULL;
		}
		MutexLocker locker(&psscf->_mutex);
		locker.lock();
		int res;
		for (map<int, SSocket *>::const_iterator iter = psscf->_sockets.begin(); \
				psscf->_sockets.end() != iter; iter ++) {
			struct epoll_event ev;
			ev.events = EPOLLIN;
			ev.data.fd = iter->first;
			res = epoll_ctl(epfd, EPOLL_CTL_ADD, iter->first, &ev);
			if (-1 == res) {
				LOG4CXX_ERROR(psscf->_logger, "epoll ctl error, errno:" << errno);
				close(epfd);
				LOG4CXX_WARN(psscf->_logger, "main svc thread exited");
				return NULL;
			}
		}
		epoll_event ev;
		res = epoll_wait(epfd, &ev, 1, 1);
		close(epfd);
		if (res < 0) {
			LOG4CXX_ERROR(psscf->_logger, "epoll wait error, res:" << res << ", errno:" << errno);
			LOG4CXX_WARN(psscf->_logger, "main svc thread exited");
			return NULL;
		}
		if (0 == res) {
			locker.unlock();
		//	sleep(DEFAULT_EPOLL_INTERVAL);
			continue;
		}
		map<int, SSocket *>::iterator iter = psscf->_sockets.find(ev.data.fd);
		SSocket * ps = iter->second; 
		psscf->_sockets.erase(ps->get_fd());
		locker.unlock();
		res = psscf->_read_evnet_handler(ps, psscf->_read_evnet_handler_arg);
		if (res <= 0) {
			ps->close();
			delete ps;
			continue;
		}
		locker.lock();
		psscf->_sockets.insert(map<int, SSocket *>::value_type(ps->get_fd(), ps));
		locker.unlock();
	}
	LOG4CXX_WARN(psscf->_logger, "main svc thread exited");
	return NULL;
}
